package com.weatherinczbackend;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.Objects;
import com.weatherinczbackend.exceptions.InternalErrorException;
import com.weatherinczbackend.exceptions.NotAllowedToRewriteFileException;
import com.weatherinczbackend.exceptions.NullObjectException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Object which can create File from URL.
 * 
 * @author Bc. Michal Stava 255739 <255739@mail.muni.cz>
 */
public class UrlToFileConvertor {
    
    //Table start, end and attribute patterns
    private static final Pattern attribute = Pattern.compile("[=][^ \">]+");
    private static final Pattern startTable = Pattern.compile("[<]table [^>]+[>]");
    private static final Pattern endTable = Pattern.compile("[<][/]((table)|(TABLE))[>]");
    
    //Error correction patterns (specific corrections, there is no problem if not exist)
    private static final Pattern errorWithTd = Pattern.compile("[\"][3][\"][>]liberecky[<]td[>]");
    private static final Pattern errorWithDoubleEndTd = Pattern.compile("[<][/]td[>][<][/]td[>]");
    
    /**
     * url - url from which will table be parsed
     */
    private URL url;
    /**
     * file - to which table will be parsed
     */
    private File file;
    /**
     * outputCharset - charset of output file
     */
    private String outputCharset;
    /**
     * inputCharset - charset of url page
     */
    private String inputCharset;
    
    /**
     * Constructor 1
     * - url is set (probably page of chmi.cz where table with weather is)
     * - file of output set (ex. weather.txt)
     * - input/outputCharsets set on UTF-8 (automatic)
     * 
     * @param url
     * @param file 
     */
    public UrlToFileConvertor(URL url, File file) {
      this.url = url;
      this.file = file;
      this.outputCharset = "UTF-8";
      this.outputCharset = "UTF-8";
    }
    
    /**
     * Constructor 2
     * - url is set (probably page of chmi.cz where table with weather is)
     * - file of output set (ex. weather.txt)
     * - input/outputCharsets set on inputCharset and outputCharset manual
     *  
     * @param url
     * @param file
     * @param outputCharset
     * @param inputCharset 
     */
    public UrlToFileConvertor(URL url, File file, String outputCharset, String inputCharset) {
      this.url = url;
      this.file = file;
      this.outputCharset = outputCharset;
      this.inputCharset = inputCharset;
    }

    /**
     * Get method for url
     * @return url
     */
    public URL getUrl() {
        return url;
    }

    /**
     * Set method for url
     * @param url 
     */
    public void setUrl(URL url) {
        this.url = url;
    }

    /**
     * Get method for file
     * @return file
     */
    public File getFile() {
        return file;
    }

    /**
     * Set method for file
     * @param file 
     */
    public void setFile(File file) {
        this.file = file;
    }

    /**
     * Get method for outputCharset
     * @return outputCharset
     */
    public String getOutputCharset() {
        return outputCharset;
    }

    /**
     * Set method for outputCharset
     * @param outputCharset 
     */
    public void setOutputCharset(String outputCharset) {
        this.outputCharset = outputCharset;
    }

    /**
     * Get method for inputCharset
     * @return 
     */
    public String getInputCharset() {
        return inputCharset;
    }

    /**
     * Set method for inputCharset
     * @param inputCharset 
     */
    public void setInputCharset(String inputCharset) {
        this.inputCharset = inputCharset;
    }
 
    /**
     * Set outputCharset to UTF-8
     */
    public void setOutputCharsetToUTF8() {
        this.outputCharset = "UTF-8";
    }
    
    /**
     * Set inputCharset to UTF-8
     */
    public void setInputCharsetToUTF8() {
        this.inputCharset = "UTF-8";
    }
    
    /**
     * Set outputCharset to ISO 8859_2
     */
    public void setOutputCharsetToISO_8859_2() {
        this.outputCharset = "8859_2";
    }
    
    /**
     * Set inputCharset to ISO 8859_2
     */
    public void setInputCharsetToISO_8859_2() {
        this.inputCharset = "8859_2";
    }
    
    /**
     * Method for hashCode.
     * @return hash int
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Objects.hashCode(this.url);
        hash = 97 * hash + Objects.hashCode(this.file);
        hash = 97 * hash + Objects.hashCode(this.outputCharset);
        hash = 97 * hash + Objects.hashCode(this.inputCharset);
        return hash;
    }

    /**
     * Equals method this object with obj.
     * @param obj
     * @return true if object are equivalent, false if not
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final UrlToFileConvertor other = (UrlToFileConvertor) obj;
        if (!Objects.equals(this.url, other.url)) {
            return false;
        }
        if (!Objects.equals(this.file, other.file)) {
            return false;
        }
        if (!Objects.equals(this.outputCharset, other.outputCharset)) {
            return false;
        }
        if (!Objects.equals(this.inputCharset, other.inputCharset)) {
            return false;
        }
        return true;
    }

    /**
     * This obj to string.
     * @return this obj in string
     */
    @Override
    public String toString() {
        return this.getClass().getSimpleName() + "{" + "url=" + url + ", file=" + file + ", outputCharset=" + outputCharset + ", inputCharset=" + inputCharset + '}';
    }
    
    /**
     * Create "file" with "outputCharset" from "url" with "inputCharset".
     * If rewriteAllowed is true, rewrite file if already exists. If not, throw Exception.
     * 
     * -- Take data from URL, parse only Table, lowerCase all alphanumeric symbols and fix correct form for parsing
     * 
     * @param rewriteAllowed if true, rewrite if exists, if not, dont rewrite if exists, only create if not exists
     * 
     * @return File of outputFile
     *
     * @throws InternalErrorException when some internal error occurs
     * @throws IOException input output exception when works with files 
     */
    public File createFileFromUrl(boolean rewriteAllowed) throws InternalErrorException, IOException {
        if(this.file == null) throw new NullObjectException("Can't create file from url, there is no file set.", "file");
        if(this.url == null) throw new NullObjectException("Can't create file from not set url.", "url");
        if(this.inputCharset == null) this.setInputCharsetToUTF8();
        if(this.outputCharset == null) this.setOutputCharsetToUTF8();
        
        File fileFromUrl = new File(url.getFile());
        InputStream is=url.openStream();
        BufferedReader br=new BufferedReader(new InputStreamReader(is, inputCharset));    
        
        if(this.file.exists() && rewriteAllowed == false) throw new NotAllowedToRewriteFileException("The file already exists and rewrite is not allowed.", file);
        if(!this.file.exists()) this.file.createNewFile();
        FileWriter fw = new FileWriter(this.file.getAbsoluteFile());
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.file), outputCharset));
            
            
        String strLine;
        boolean tableFound = false;
        boolean canBeBadTrEndTag = false;
        while((strLine = br.readLine()) != null) {
            strLine = strLine.toLowerCase();
            strLine = this.removeCzechDiacritic(strLine);
            strLine = strLine.replaceAll("[&]nbsp[;]", "");
            Matcher attr = attribute.matcher(strLine);
            while(attr.find()) {
                String str = attr.group();
                str = "=" + str.replace("=", "\"") + "\"";
                strLine = strLine.replaceFirst("[=][^ \">]+", str);
            }
            
            //Error correction cause not ended <td> in some tables
            Matcher errorCorrection1 = errorWithTd.matcher(strLine);
            if(errorCorrection1.find()) strLine = strLine.replaceAll("[\"][3][\"][>]liberecky", "\"3\">liberecky</td>");
            //Error correction cause double ended </td> in a row
            Matcher errorCorrection2 = errorWithDoubleEndTd.matcher(strLine);
            if(errorCorrection2.find()) strLine = strLine.replaceAll("[<][/]td[>][<][/]td[>]", "</td>");
            //Error correction cause missing ended </tr>
            if(canBeBadTrEndTag) {
                if(strLine.startsWith("<tr")) strLine = "</tr>" + strLine;
                canBeBadTrEndTag = false;
            }
            else if(strLine.endsWith("</td>")) canBeBadTrEndTag = true; 
           
            if(!tableFound) {
                
                Matcher st = startTable.matcher(strLine);
                if(st.find()) {
                    bw.write(strLine.substring(st.start()));
                    if(!strLine.endsWith("td>")) bw.write('\n');
                    tableFound = true;
                }
            } else {
                Matcher et = endTable.matcher(strLine);
                if(et.find()) {
                    bw.write(strLine.substring(0, et.end()));
                    break;
                } else {
                    bw.write(strLine);
                    if(!strLine.endsWith("td>")) bw.write('\n');
                }
            }
        }
        is.close();
        fw.close();
        br.close();
        bw.close();
        return this.file;
    }
    
    public void deleteTemporaryFile() throws IOException, InternalErrorException {
      if(file == null) throw new NullObjectException("File is null, can't delete it.", "file");
      file.delete();
      
    }
    
    /**
     * All czech diacritic symbols replace by normal nondiacritic symbols.
     * 
     * @param string text with probably diacritic symbols
     * @return return string without czech diacritic symbols
     */
    private String removeCzechDiacritic(String string) {
        if(string == null) return "";
        if(string.isEmpty()) return string;
        string = string.replaceAll("á", "a");    
        string = string.replaceAll("ä", "a");
        string = string.replaceAll("č", "c");
        string = string.replaceAll("ç", "c");
        string = string.replaceAll("ď", "d");
        string = string.replaceAll("é", "e");
        string = string.replaceAll("ě", "e");
        string = string.replaceAll("ë", "e");
        string = string.replaceAll("í", "i");
        string = string.replaceAll("ľ", "l");
        string = string.replaceAll("ň", "n");
        string = string.replaceAll("ó", "o");
        string = string.replaceAll("ö", "o");
        string = string.replaceAll("ř", "r");
        string = string.replaceAll("š", "s");
        string = string.replaceAll("ť", "t");
        string = string.replaceAll("ú", "u");
        string = string.replaceAll("ů", "u");
        string = string.replaceAll("ü", "u");
        string = string.replaceAll("ý", "y");
        string = string.replaceAll("ž", "z");
        return string;
    }
}
